# dbplugs/karma.py
#
#

""" database karma plugin """

__copyright__ = 'this file is in the public domain'

from gozerbot.generic import rlog
from gozerbot.commands import cmnds
from gozerbot.examples import examples
from gozerbot.redispatcher import rebefore
from gozerbot.db import db
from gozerbot.statdict import Statdict
from gozerbot.plughelp import plughelp
from gozerbot.aliases import aliases
from plugs.links import links
import time

plughelp.add('karma', 'maintain karma of items .. use ++ to raise karma by 1 \
or use -- to lower by 1 .. reason might be given after a "#"')

class Karma(object):

    """ karma object """

    def size(self):
        nritems = db.execute(""" SELECT COUNT(*) FROM karma """)
        return nritems[0][0]

    def get(self, item):
        """ get karma of item """
        item = item.lower()
        value = db.execute(""" SELECT value FROM karma WHERE item = %s """, \
item)
        if value:
            return value[0][0]
        else:
            return 0

    def delete(self, item):
        """ delete karma item """
        item = item.lower()
        value = db.execute(""" DELETE FROM karma WHERE item = %s """, item)
        return value

    def addwhy(self, item, updown, reason):
        """ add why of karma up/down """
        item = item.lower()
        result = db.execute(""" INSERT INTO whykarma(item, updown, why) \
VALUES (%s, %s, %s) """, (item, updown, reason))
        return result
            
    def upitem(self, item, reason=None):
        """ up a karma item with/without reason """
        item = item.lower()
        value = db.execute(""" SELECT value FROM karma WHERE item = %s """, \
item)
        try:
            val = value[0][0]
        except TypeError:
            result = db.execute(""" INSERT INTO karma(item, value) VALUES \
(%s, 1) """, item)
            if reason:
                self.addwhy(item, 'up', reason.strip())
            return result
        val += 1
        result = db.execute(""" UPDATE karma SET value = %s WHERE item = %s \
""", (val, item))
        if reason:
            self.addwhy(item, 'up', reason.strip())
        return result

    def down(self, item, reason=None):
        """ lower a karma item with/without reason """
        item = item.lower()
        value = db.execute(""" SELECT value FROM karma WHERE item = %s """, \
item)
        try:
            val = value[0][0]
        except TypeError:
            result = db.execute(""" INSERT INTO karma(item, value) VALUES \
(%s, -1) """, item)
            if reason:
                self.addwhy(item, 'down', reason.strip())
            return result
        val -= 1
        result = db.execute(""" UPDATE karma SET value = %s WHERE item = %s \
""", (val, item))
        if reason:
            self.addwhy(item, 'down', reason.strip())
        return result

    def whykarmaup(self, item):
        """ get why of karma ups """
        item = item.lower()
        result = db.execute(""" SELECT why FROM whykarma WHERE \
item = %s AND updown = 'up' """, item)
        return result

    def whykarmadown(self, item):
        """ get why of karma downs """
        item = item.lower()
        result = db.execute(""" SELECT why FROM whykarma WHERE \
item = %s AND updown = 'down' """, item)
        return result

    def setwhoup(self, item, nick):
        """ set who upped a karma item """
        item = item.lower()
        nick = nick.lower()
        result = db.execute(""" INSERT INTO whokarma(item, nick, updown) \
VALUES(%s, %s, %s) """, (item, nick, 'up'))
        return result
 
    def setwhodown(self, item, nick):
        """ set who lowered a karma item """
        item = item.lower()
        nick = nick.lower()
        result = db.execute(""" INSERT INTO whokarma(item, nick, updown) \
VALUES(%s, %s, %s) """, (item, nick, 'down'))
        return result

    def getwhoup(self, item):
        """ get list of who upped a karma item """
        item = item.lower()
        result = db.execute(""" SELECT nick FROM whokarma WHERE item = %s \
AND updown = 'up' """, item)
        return result

    def getwhodown(self, item):
        """ get list of who downed a karma item """
        item = item.lower()
        result = db.execute(""" SELECT nick FROM whokarma WHERE item = %s \
AND updown = 'down' """, item)
        return result

    def search(self, item):
        """ search for matching karma item """
        item = item.lower()
        result = db.execute(""" SELECT item FROM karma WHERE item LIKE %s \
""", '%%%s%%' % item)
        return result

    def good(self, limit=10):
        """ show top 10 of karma items """
        statdict = Statdict()
        result = db.execute(""" SELECT item, value FROM karma """)
        if not result:
            return []
        for i in result:
            statdict.upitem(i[0], value=i[1])
        return statdict.top(limit=limit)

    def bad(self, limit=10):
        """ show lowest 10 of negative karma items """
        statdict = Statdict()
        result = db.execute(""" SELECT item, value FROM karma """)
        if not result:
            return []
        for i in result:
            statdict.upitem(i[0], value=i[1])
        return statdict.down(limit=limit)

karma = Karma()

ratelimited = []

def ratelimit(bot, ievent):
    """ karma rate limiter """
    waittime = 30
    limit = 2
    userhost = ievent.userhost
    # Create a state for this user and his/her karma-state if necessary
    if not bot.state.has_key(userhost):
        bot.state[userhost] = {}
    if not bot.state[userhost].has_key('karma'):
        bot.state[userhost]['karma'] = {'count': 0, 'time': time.time() } 
    # If the waittime has elapsed, reset the counter
    if time.time() > bot.state[userhost]['karma']['time'] + waittime:
        bot.state[userhost]['karma']['count'] = 0 
    # Update counter
    bot.state[userhost]['karma']['count'] += 1
    # If counter is too high, limit :)
    if bot.state[userhost]['karma']['count'] > limit:
        if userhost in ratelimited:
            return 0
        ievent.reply("karma limit reached, you'll have to wait %s \
seconds" % int(bot.state[userhost]['karma']['time'] + waittime - time.time()))
        ratelimited.append(userhost)
        rlog(10, 'karma', '%s karma ratelimited' % ievent.nick)
        return 0
    # Update time
    bot.state[userhost]['karma']['time'] = time.time()
    # Ratelimiting passed :)
    try:
        ratelimited.remove(userhost)
    except ValueError:
        pass
    return 1

def handle_karmaget(bot, ievent):
    """karma-get <item> .. show karma of item """
    if not ievent.rest:
        ievent.missing('<item>')
        return
    else:
        item = ievent.rest
    linklist = links[item]
    linkresult = ""
    total = 0
    if linklist:
        for i in linklist:
            linkkarma = karma.get(i)
            if linkkarma:
                linkresult += "%s (%s) " % (i, linkkarma)
                total += linkkarma
    result = karma.get(item)
    if result:
        if linkresult:
            total += result
            ievent.reply("%s has karma %s .. %s .. total is %s" % (item, \
str(result), linkresult, total))
        else:
            ievent.reply("%s has karma %s" % (item, str(result)))
    else:
        ievent.reply("%s has no karma yet" % item)

cmnds.add('karma-get', handle_karmaget, ['USER', 'WEB'])
examples.add('karma-get', 'karma-get <item> .. show karma of <item>', \
'karma-get dunker')
aliases.data['karma'] = 'karma-get'

def handle_karmadel(bot, ievent):
    """ karma-del <item> .. delete karma item """
    if not ievent.rest:
        ievent.missing('<item>')
        return
    item = ievent.rest
    result = karma.delete(item)
    if result:
        ievent.reply("%s deleted" % item)
    else:
        ievent.reply("can't delete %s" % item)

cmnds.add('karma-del', handle_karmadel, 'OPER')
examples.add('karma-del', 'karma-del <item> .. delete karma item', \
'karma-del dunker')

def handle_karmaup(bot, ievent, groups):
    """ <item>++ ['#' <reason>] .. increase karma of item with reason """
    if not ratelimit(bot, ievent):
        return
    (item, reason) = groups
    item = item.strip().lower()
    karma.upitem(item, reason=reason)
    karma.setwhoup(item, ievent.nick)
    ievent.reply(item + ' has now karma ' + str(karma.get(item)))

rebefore.add(10, '^(.+)\+\+\s+#(.*)$', handle_karmaup, 'USER')
examples.add('++', "<item>++ ['#' <reason>] // higher karma of item with 1 \
(use optional reason)", '1) gozerbot++ 2) gozerbot++ # top bot')

def handle_karmaup2(bot, ievent, groups):
    """ increase karma without reason """
    grouplist = list(groups) + [None, ]
    handle_karmaup(bot, ievent, grouplist)

rebefore.add(11, '^(.+)\+\+$', handle_karmaup2, 'USER')

def handle_karmadown(bot, ievent, groups):
    """ <item>-- ['#' <reason>] .. decrease karma item with reason """
    if not ratelimit(bot, ievent):
        return
    (item, reason) = groups
    item = item.strip().lower()
    karma.down(item, reason=reason)
    karma.setwhodown(item, ievent.nick)
    ievent.reply(item + ' has now karma ' + str(karma.get(item)))

rebefore.add(10, '^(.+)\-\-\s+#(.*)$', handle_karmadown, 'USER')
examples.add('--', "<item>-- ['#' <reason>] .. lower karma of item with 1 \
(use optional reason)", '1) gozerbot-- 2) gozerbot-- # bad bot')

def handle_karmadown2(bot, ievent, groups):
    """ decrease karma item without reason """
    grouplist = list(groups) + [None, ]
    handle_karmadown(bot, ievent, grouplist)

rebefore.add(10, '^(.+)\-\-$', handle_karmadown2, 'USER')

def handle_whykarmaup(bot, ievent):
    """ karma-whyup <item> .. show why karma of item has been increased """
    if not ievent.rest:
        ievent.missing('<item>')
        return
    item = ievent.rest
    result = karma.whykarmaup(item)
    if result:
        res = ""
        for i in result:
            res += "%s .. " % i[0]
        ievent.reply('whykarmaup of %s: %s' % (item, res[:-4]))
    else:
        ievent.reply('%s has no reason for karmaup yet' % item)

cmnds.add('karma-whyup', handle_whykarmaup, ['USER', 'WEB'])
examples.add('karma-whyup', 'karma-whyup <item> .. show the reason why \
karma of <item> was raised', 'karma-whyup gozerbot')
aliases.data['wku'] = 'karma-whyup'

def handle_whykarmadown(bot, ievent):
    """ karma-whydown <item> .. show why karma of item has been decreased """
    if not ievent.rest:
        ievent.missing('<item>')
        return
    item = ievent.rest
    result = karma.whykarmadown(item)
    if result:
        res = ""
        for i in result:
            res += "%s .. " % i[0]
        ievent.reply('whykarmadown of %s: %s' % (item, res[:-4]))
    else:
        ievent.reply('%s has no reason for karmadown yet' % item)

cmnds.add('karma-whydown', handle_whykarmadown, ['USER', 'WEB'])
examples.add('karma-whydown', 'karma-whydown <item> .. show the reason why \
karma of <item> was lowered', 'karma-whydown gozerbot')
aliases.data['wkd'] = 'karma-whydown'

def handle_karmagood(bot, ievent):
    """ karma-good .. show top 10 karma items """
    result = karma.good(limit=10)
    if result:
        resultstr = ""
        for i in result:
            if i[1] > 0:
                resultstr += "%s: %s " % (i[0], i[1])
        ievent.reply('goodness = %s' % resultstr)
    else:
        ievent.reply('karma void')

cmnds.add('karma-good', handle_karmagood, ['USER', 'WEB'])
examples.add('karma-good', 'show top 10 karma', 'good')
aliases.data['good'] = 'karma-good'

def handle_karmabad(bot, ievent):
    """ karma-bad .. show 10 most negative karma items """
    result = karma.bad(limit=10)
    if result:
        resultstr = ""
        for i in result:
            if i[1] < 0:
                resultstr += "%s: %s " % (i[0], i[1])
        ievent.reply('badness = %s' % resultstr)
    else:
        ievent.reply('karma void')

cmnds.add('karma-bad', handle_karmabad, ['USER', 'WEB'])
examples.add('karma-bad', 'show lowest top 10 karma', 'karma-bad')
aliases.data['bad'] = 'karma-bad'

def handle_karmasearch(bot, ievent):
    """ karma-search <txt> .. search for karma items """
    what = ievent.rest
    if not what:
        ievent.missing('<txt>')
        return 
    result = karma.search(what)
    if result:
        resultstr = ""
        for i in result:
            resultstr += "%s .. " % i[0]
        ievent.reply(resultstr[:-4])
    else:
        ievent.reply('no karma items matching %s found' % what)

cmnds.add('karma-search', handle_karmasearch, ['USER', 'WEB'])
examples.add('karma-search', 'karma-search <txt> .. search karma' , \
'karma-search gozerbot')

def handle_whokarmaup(bot, ievent):
    """ karma-whoup <item> .. show who increased a karma item """
    if not ievent.rest:
        ievent.missing('<item>')
        return
    item = ievent.rest
    result = karma.getwhoup(item)
    statdict = Statdict()
    if result:
        for i in result:
            statdict.upitem(i[0])
        resultstr = ""
        for i in statdict.top():
            resultstr += "%s = %s " % i
        ievent.reply(resultstr)
    else:
        ievent.reply('no whokarmaup data available for %s' % item)

cmnds.add('karma-whoup', handle_whokarmaup, ['USER', 'WEB'])
examples.add('karma-whoup', 'karma-whoup <item> .. show who raised the \
karma of <item>', 'karma-whoup gozerbot')

def handle_whokarmadown(bot, ievent):
    """ karma-whodown <item> .. show who decreased a karma item """
    if not ievent.rest:
        ievent.missing('<item>')
        return
    item = ievent.rest
    result = karma.getwhodown(item)
    statdict = Statdict()
    if result:
        for i in result:
            statdict.upitem(i[0])
        resultstr = ""
        for i in statdict.top():
            resultstr += "%s = %s " % i
        ievent.reply(resultstr)
    else:
        ievent.reply('no whokarmadown data available for %s' % item)

cmnds.add('karma-whodown', handle_whokarmadown, ['USER', 'WEB'])
examples.add('karma-whodown', 'karma-whodown <item> .. show who lowered \
the karma of <item>', 'karma-whodown gozerbot')
